package dao

import (
	"bytes"
	"context"
	"fmt"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/ecodeclub/ekit"
	"github.com/spf13/viper"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"jk-time/webook/internal/domain"
	"strconv"
	"time"
)

type S3Dao struct {
	oss *s3.S3
	*GormArticleDao
	bucket *string
}

func NewS3Dao(oss *s3.S3, gormArticleDao *GormArticleDao) ArticleDao {
	bucket := viper.GetString("oss.bucket")
	return &S3Dao{oss: oss, GormArticleDao: gormArticleDao, bucket: &bucket}
}

// 只需考虑线上库
func (o *S3Dao) Sync(ctx context.Context, art Article) (int64, error) {
	var publishedArticle PublishedArticle
	//先操作制作库，后操作线上库
	err := o.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		var err error
		txDao := NewGormArticleDao(tx)
		if art.Id > 0 {
			err = txDao.UpdateById(ctx, art)
		} else {
			art.Id, err = txDao.Insert(ctx, art)
		}
		if err != nil {
			return err
		}

		//操作线上库,插入/更新：解决并发问题
		publishedArticle = PublishedArticle(art)
		art.Content = ""
		return o.Upsert(ctx, PublishedArticle(art))
	})
	//在事务中保存了制作库和线上库的数据，但线上库没有保存Content,Content在oss里，前端自己获取
	_, err = o.oss.PutObjectWithContext(ctx, &s3.PutObjectInput{
		Bucket:      o.bucket,
		Key:         ekit.ToPtr[string](strconv.FormatInt(art.Id, 10)),
		Body:        bytes.NewReader([]byte(publishedArticle.Content)),
		ContentType: ekit.ToPtr[string]("text/plain;charset=utf-8"),
	})
	if err != nil {
		zap.L().Error("保存oss失败：", zap.Error(err))
	}
	return art.Id, nil
}
func (o *S3Dao) SyncStatus(ctx context.Context, art Article) error {
	now := time.Now().UnixMilli()
	err := o.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		res := tx.Model(&PublishedArticle{}).
			Where("id=? and author_id=?", art.Id, art.AuthorId).
			Updates(map[string]any{
				"status": art.Status,
				"utime":  now,
			})
		if res.Error != nil {
			return res.Error
		}
		if res.RowsAffected != 1 {
			return fmt.Errorf("误操作非自己的文章,uid: %d authorId: %d", art.Id, art.AuthorId)
		}
		return nil
	})
	if err != nil {
		return err
	}
	//删除oss内容，但cdn的未删除
	if art.Status == domain.ArticleStatusPrivate.ToUint8() {
		_, err = o.oss.DeleteObjectWithContext(ctx, &s3.DeleteObjectInput{
			Bucket: o.bucket,
			Key:    ekit.ToPtr[string](strconv.FormatInt(art.Id, 10)),
		})
	}
	return nil
}
